import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
import lombok.Data;
import lombok.SneakyThrows;
import org.apache.commons.lang3.RandomUtils;
import org.apache.poi.ss.formula.functions.T;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

public class BasicPaxos {


    private static List<Acceptor> Acceptors = new ArrayList<>();
    private static final int acceptorsNum =5;

    static class Proposer extends Thread{
        private final int no;
        private final int time;
        Proposer(int no, int time){
            this.no = no;
            this.time = time;
        }

        @Override
        public String toString() {
            return "[Proposer:" + no;
        }

        @SneakyThrows
        @Override
        public void run() {
            Thread.sleep((long) (Math.random()*time));

            List<Future<Proposal>> results = new ArrayList<>();
            for (Acceptor acceptor : Acceptors) {
                new Thread(){  //内部类Proposer的匿名内部类
                    @Override
                    @SneakyThrows
                    public void run(){
                        Thread.sleep(new Random().nextInt(10000));
                        acceptor.prepare(no, Proposer.this);//内部类可以通过.this引用外部类的对象
                    }
                }.start();
            }
            while (true){
                Thread.sleep(new Random().nextInt(10000));
            }
        }

        private boolean sendProposal = false;
        private List<Proposal> proposals = new ArrayList<>();
        public synchronized void receivePrepare(Proposal proposal){
            proposals.add(proposal);
            if (!sendProposal){
                if (proposals.size() > acceptorsNum/2){    //当收到大多数节点的响应时，尝试发送提案
                    int value = proposals.stream().max(Comparator.comparingInt(Proposal::getNo)).get().getValue();
                    if (value == 0){
                        value = RandomUtils.nextInt(1, 10);
                        System.out.println("Proposer" + no +"生成了自己的值:" + value);
                    }else{
                        System.out.println("Proposer" + no +"使用了别人的值:" + value);
                    }
                    for (Acceptor acceptor : Acceptors) {
                        int finalValue = value;
                        new Thread(){  //内部类Proposer的匿名内部类
                            @Override
                            @SneakyThrows
                            public void run(){
                                Thread.sleep(new Random().nextInt(10000));
                                acceptor.accept(new Proposal(no, finalValue));//内部类可以通过.this引用外部类的对象
                            }
                        }.start();
                    }
                    sendProposal = true;
                }
            }
        }
    }

    @Data
    static class Acceptor{
        private String name;
        Acceptor(int name){
            this.name = String.valueOf(name);
        }

        private int minProposal = 0;
        private Proposal proposal = new Proposal();

        private synchronized void prepare(int no, Proposer proposer){
            //准备阶段：
            if (no > minProposal){
                minProposal = no;
                System.out.println("Prepare:Acceptor"+name+": accept " + proposer);
                new Thread(() -> proposer.receivePrepare(proposal.clone())).start();
                return;
            }
            System.out.println("Prepare:Acceptor"+name+": reject " + proposer);
        }

        private synchronized void accept(Proposal proposal){
            //接受阶段：
            if (proposal.getNo() >= minProposal){
                this.proposal = proposal;
                System.out.println("Accept:Acceptor"+name+": accept " + proposal);
                return;
            }
            System.out.println("Accept:Acceptor"+name+": reject " + proposal);
        }
    }

    @Data
    static class Proposal implements Cloneable, Serializable {
        int no;
        int value;
        Proposal(){
        }
        Proposal(int no){
            this.no = no;
        }
        Proposal(int no, int value){
            this.no = no;
            this.value = value;
        }

        @Override
        protected Proposal clone(){
            try {
                return (Proposal) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 5; i++) {
            Acceptors.add(new Acceptor(i));
        }
        new Proposer(1,1000).start();
        new Proposer(2, 10000).start();
        while (true){
            Thread.sleep(10000);
            System.out.println("巡检时间：——————————————————");
            for (Acceptor acceptor : Acceptors) {
                System.out.println(acceptor.getProposal().value);
            }
            System.out.println("巡检时间：——————————————————");
        }
    }
}
